package org.example.security;

import org.example.filter.TenantFilter;
import org.example.filter.TokenFilter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.factory.PasswordEncoderFactories;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.access.AccessDeniedHandler;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

@Configuration
@EnableWebSecurity(debug = true)
public class SecurityConfig {
    @Autowired
    JwtAuthenticationSecurityConfig jwtAuthenticationSecurityConfig;
    @Autowired
    AuthenticationEntryPoint entryPoint;
    @Autowired
    AccessDeniedHandler accessDeniedHandler;

    /**
     * springsecurity官方文档配置
        Security Filter 是通过 SecurityFilterChain API 插入 FilterChainProxy 中的。
     */
    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
//        http
//            .csrf(Customizer.withDefaults())
//            .authorizeHttpRequests(authorize -> authorize
//                .anyRequest().authenticated()
//            );
//        //使用自定义的前后端分离登录jwt
////            .httpBasic(Customizer.withDefaults());
////            .formLogin(Customizer.withDefaults());
//        return http.build();

        http.formLogin()
                //禁用表单登录，前后端分离用不上
                .disable()
                //应用登录过滤器的配置，配置分离
                .apply(jwtAuthenticationSecurityConfig)

                .and()
                // 设置URL的授权
                .authorizeRequests()
                // 这里需要将登录页面放行,permitAll()表示不再拦截，/login 登录的url，/refreshToken刷新token的url
                //TODO 此处正常项目中放行的url还有很多，比如swagger相关的url，druid的后台url，一些静态资源
                .antMatchers(   "/login","/refreshToken")
                .permitAll()
                //hasRole()表示需要指定的角色才能访问资源
//                .antMatchers("/hello").hasRole("ADMIN")
                // anyRequest() 所有请求   authenticated() 必须被认证
                .anyRequest()
                .authenticated()

                //处理异常情况：认证失败和权限不足
                .and()
                .exceptionHandling()
                //认证未通过，不允许访问异常处理器
                .authenticationEntryPoint(entryPoint)
                //认证通过，但是没权限处理器
                .accessDeniedHandler(accessDeniedHandler)

                .and()
                //禁用session，JWT校验不需要session
                .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)

                .and()
                //将TOKEN校验过滤器配置到过滤器链中，否则不生效，放到UsernamePasswordAuthenticationFilter之前
                .addFilterBefore(tokenFilter(), UsernamePasswordAuthenticationFilter.class)
                // 关闭csrf
                .csrf().disable();
        return http.build();
    }

    /**
     * 当你把你的 filter 声明为 Spring Bean 时要小心，可以用 @Component 注解它，也可以在配置中把它声明为 Bean，
     * 因为 Spring Boot 会自动 在嵌入式容器中注册它。
     * 这可能会导致 filter 被调用两次，一次由容器调用，一次由 Spring Security 调用，而且顺序不同。

     * 如果你仍然想把你的 filter 声明为 Spring Bean，以利用依赖注入，避免重复调用，你可以通过声明 FilterRegistrationBean Bean
     * 并将其 enabled 属性设置为 false 来告诉 Spring Boot 不要向容器注册它：
     */
    @Bean
    public FilterRegistrationBean<TenantFilter> tenantFilterRegistration(TenantFilter filter) {
        FilterRegistrationBean<TenantFilter> registration = new FilterRegistrationBean<>(filter);
        registration.setEnabled(false);
        return registration;
    }

//    @Bean
//    CustomUserDetailsService customUserDetailsService() {
//        return new CustomUserDetailsService();
//    }

    @Bean
    public PasswordEncoder passwordEncoder(){
        return PasswordEncoderFactories.createDelegatingPasswordEncoder();
//        return new BCryptPasswordEncoder();
    }

    @Bean
    TokenFilter tokenFilter(){
        return new TokenFilter();
    }
}